module RestfulController

  def self.included(base) #:nodoc:
    base.class_eval do
      cattr_accessor :resource_name
      cattr_accessor :resource_find_method, :resources_find_method
      self.resource_name ||= name.singularize
      self.resource_find_method ||= "find"
      self.resources_find_method ||= "find"
    end
    base.extend ClassMethods
  end
  
  module ClassMethods
    # Sets the class-level accessor that determines the model for this resource.
    # Defaults to the singularized name of the controller
    def set_resource(name)
      self.resource_name=name.to_s
    end

    # Sets the class-level accessor that determines the method to call to search
    # defaults to find()
    def set_resource_find_method(finder)
      self.resource_find_method=finder.to_sym
    end

    # Sets the class-level accessor that determines the method to call to search
    # defaults to find()
    def set_resources_find_method(finder)
      self.resources_find_method=finder.to_sym
    end
  end

  # GET /resources
  def index
    self.resources = Resource().send(resources_find_method, :all)
    respond_to { |format|
      format.html { render }
      format.js   { render :js => :index }
      format.xml  { render :xml => resources.to_xml }
    }
  end
  
  # GET /resources/1
  def show
    begin
      self.resource = Resource().send(resource_find_method, params[:id])
    rescue ActiveRecord::RecordNotFound
      raise Merb::Controller::NotFound
    end
    respond_to { |format|
      format.html { render }
      format.js   { render :js => :show }
      format.xml  { render :xml => resource.to_xml }
    }
  end
  
  # GET /resources/new
  def new
    self.resource = Resource().new
    respond_to { |format|
      format.html { render }
      format.js   { render :js => :new }
    }
  end
  
  # POST /resources
  def create
    self.resource = Resource().new(params[resource_sym])
    respond_to { |format|
      if resource.save
        format.html { redirect resource_url }
        format.js   { render :js => :create }
        format.xml  { redirect resource_url(:format => :xml) } # TODO: head created
      else
        format.html { render :action => "new" }
        # TODO: JS errors?
        format.xml  { render :xml => resource.errors.to_xml }
      end
    }
  end
    
  # GET /resources/1/edit
  def edit
    self.resource = Resource().send(resource_find_method, params[:id])
    respond_to { |format|
      format.html { render }
      format.js   { render :js => :edit }
    }
  end

  # PUT /resources/1
  def update
    self.resource = Resource().send(resource_find_method, params[:id])
    respond_to { |format|
      if resource.update_attributes(params[resource_sym])
        format.html { redirect resource_url }
        format.js   { render :js => :update }
        format.xml  { render :nothing => 200 }
      else
        format.html { render :action => "edit" }
        # TODO: JS errors?
        format.xml  { render :xml => resource.errors.to_xml }
      end
    }
  end

  # DELETE /resources/1
  def destroy
    self.resource = Resource().send(resource_find_method, params[:id])
    resource.destroy
    respond_to { |format|
      format.html { redirect resources_url }
      format.js   { render :js => :destroy }
      format.xml  { render :nothing => 200 }
    }
  end
  
  protected
  # Returns the class constant for the model for this resource.
  def Resource
    self.class.resource_name.constantize
  end
  
  # Returns the name of the singular version of this resource as a string
  def resource_s
    self.class.resource_name.downcase
  end
  
  # Returns the name of the plural version of this resource as a string
  def resources_s
    self.class.resource_name.pluralize.downcase
  end
  
  # Returns the name of the singular version of this resource as a symbol
  def resource_sym
    resource_s.to_sym
  end
  
  # Returns the name of the pluralize version of this resource as a symbol
  def resources_sym
    resources_s.to_sym
  end
  
  # Returns the name of the instance variable for the singular version of this resource
  def resource_ivar_name
    "@" + resource_s
  end

  # Returns the name of the instance variable for the plural version of this resource
  def resources_ivar_name
    "@" + resources_s
  end

  # Returns the value of the instance variable for the singular version of this resource
  def resource
    instance_variable_get resource_ivar_name
  end

  # Returns the value of the instance variable for the plural version of this resource
  def resources
    instance_variable_get resources_ivar_name
  end

  # Sets the instance variable for the singular version of this resource
  def resource=(value)
    instance_variable_set resource_ivar_name, value
  end

  # Sets the instance variable for the plural version of this resource
  def resources=(value)
    instance_variable_set resources_ivar_name, value
  end
  
  # Returns the helper for the singular URL
  def resource_url(*opts)
    url(resource_sym,resource,*opts)
  end
  
  # Returns the helper for the plural URL
  def resources_url(*opts)
    url(resources_sym,*opts)
  end
  
end
